home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / fileutil.13 / fileutil / fileutils-3.13 / lib / makepath.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  7.3 KB  |  303 lines

  1. /* makepath.c -- Ensure that a directory path exists.
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
  19.    Jim Meyering <meyering@cs.utexas.edu>.  */
  20.  
  21. #ifdef HAVE_CONFIG_H
  22. #include <config.h>
  23. #endif
  24.  
  25. #ifdef __GNUC__
  26. #define alloca __builtin_alloca
  27. #else
  28. #ifdef HAVE_ALLOCA_H
  29. #include <alloca.h>
  30. #else
  31. #ifdef _AIX
  32.  #pragma alloca
  33. #else
  34. char *alloca ();
  35. #endif
  36. #endif
  37. #endif
  38.  
  39. #include <stdio.h>
  40. #include <sys/types.h>
  41. #include <sys/stat.h>
  42. #ifdef HAVE_UNISTD_H
  43. #include <unistd.h>
  44. #endif
  45.  
  46. #ifdef STAT_MACROS_BROKEN
  47. #undef S_ISDIR
  48. #endif /* STAT_MACROS_BROKEN.  */
  49.  
  50. #if !defined(S_ISDIR) && defined(S_IFDIR)
  51. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  52. #endif
  53.  
  54. #ifdef STDC_HEADERS
  55. #include <stdlib.h>
  56. #endif
  57.  
  58. #ifdef HAVE_ERRNO_H
  59. #include <errno.h>
  60. #endif
  61.  
  62. #ifndef errno
  63. extern int errno;
  64. #endif
  65.  
  66. #ifdef HAVE_STRING_H
  67. # include <string.h>
  68. #else
  69. # include <strings.h>
  70. # ifndef strchr
  71. #  define strchr index
  72. # endif
  73. #endif
  74.  
  75. #ifdef __MSDOS__
  76. typedef int uid_t;
  77. typedef int gid_t;
  78. #endif
  79.  
  80. #include "makepath.h"
  81.  
  82. void error ();
  83.  
  84. /* Ensure that the directory ARGPATH exists.
  85.    Remove any trailing slashes from ARGPATH before calling this function.
  86.  
  87.    Create any leading directories that don't already exist, with
  88.    permissions PARENT_MODE.
  89.    If the last element of ARGPATH does not exist, create it as
  90.    a new directory with permissions MODE.
  91.    If OWNER and GROUP are non-negative, use them to set the UID and GID of
  92.    any created directories.
  93.    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
  94.    string for printing a message after successfully making a directory,
  95.    with the name of the directory that was just made as an argument.
  96.    If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
  97.    then do not attempt to set its permissions and ownership.
  98.  
  99.    Return 0 if ARGPATH exists as a directory with the proper
  100.    ownership and permissions when done, otherwise 1.  */
  101.  
  102. #if __STDC__
  103. int
  104. make_path (const char *argpath,
  105.        int mode,
  106.        int parent_mode,
  107.        uid_t owner,
  108.        gid_t group,
  109.        int preserve_existing,
  110.        const char *verbose_fmt_string)
  111. #else
  112. int
  113. make_path (argpath, mode, parent_mode, owner, group, preserve_existing,
  114.        verbose_fmt_string)
  115.      const char *argpath;
  116.      int mode;
  117.      int parent_mode;
  118.      uid_t owner;
  119.      gid_t group;
  120.      int preserve_existing;
  121.      const char *verbose_fmt_string;
  122. #endif
  123. {
  124.   char *dirpath;        /* A copy we can scribble NULs on.  */
  125.   struct stat stats;
  126.   int retval = 0;
  127.   int oldmask = umask (0);
  128.  
  129.   /* FIXME: move this alloca and strcpy into the if-block.
  130.      Set dirpath to argpath in the else-block.  */
  131.   dirpath = (char *) alloca (strlen (argpath) + 1);
  132.   strcpy (dirpath, argpath);
  133.  
  134.   if (stat (dirpath, &stats))
  135.     {
  136.       char *slash;
  137.       int tmp_mode;        /* Initial perms for leading dirs.  */
  138.       int re_protect;        /* Should leading dirs be unwritable? */
  139.       struct ptr_list
  140.       {
  141.     char *dirname_end;
  142.     struct ptr_list *next;
  143.       };
  144.       struct ptr_list *p, *leading_dirs = NULL;
  145.  
  146.       /* If leading directories shouldn't be writable or executable,
  147.      or should have set[ug]id or sticky bits set and we are setting
  148.      their owners, we need to fix their permissions after making them.  */
  149.       if (((parent_mode & 0300) != 0300)
  150.       || (owner != (uid_t) -1 && group != (gid_t) -1
  151.           && (parent_mode & 07000) != 0))
  152.     {
  153.       tmp_mode = 0700;
  154.       re_protect = 1;
  155.     }
  156.       else
  157.     {
  158.       tmp_mode = parent_mode;
  159.       re_protect = 0;
  160.     }
  161.  
  162.       slash = dirpath;
  163.       while (*slash == '/')
  164.     slash++;
  165.       while ((slash = strchr (slash, '/')))
  166.     {
  167.       *slash = '\0';
  168.       if (stat (dirpath, &stats))
  169.         {
  170.           if (mkdir (dirpath, tmp_mode))
  171.         {
  172.           error (0, errno, "cannot create directory `%s'", dirpath);
  173.           umask (oldmask);
  174.           return 1;
  175.         }
  176.           else
  177.         {
  178.           if (verbose_fmt_string != NULL)
  179.             error (0, 0, verbose_fmt_string, dirpath);
  180.  
  181.           if (owner != (uid_t) -1 && group != (gid_t) -1
  182.               && chown (dirpath, owner, group)
  183. #if defined(AFS) && defined (EPERM)
  184.               && errno != EPERM
  185. #endif
  186.               )
  187.             {
  188.               error (0, errno, "%s", dirpath);
  189.               retval = 1;
  190.             }
  191.           if (re_protect)
  192.             {
  193.               struct ptr_list *new = (struct ptr_list *)
  194.             alloca (sizeof (struct ptr_list));
  195.               new->dirname_end = slash;
  196.               new->next = leading_dirs;
  197.               leading_dirs = new;
  198.             }
  199.         }
  200.         }
  201.       else if (!S_ISDIR (stats.st_mode))
  202.         {
  203.           error (0, 0, "`%s' exists but is not a directory", dirpath);
  204.           umask (oldmask);
  205.           return 1;
  206.         }
  207.  
  208.       *slash++ = '/';
  209.  
  210.       /* Avoid unnecessary calls to `stat' when given
  211.          pathnames containing multiple adjacent slashes.  */
  212.       while (*slash == '/')
  213.         slash++;
  214.     }
  215.  
  216.       /* We're done making leading directories.
  217.      Create the final component of the path.  */
  218.  
  219.       /* The path could end in "/." or contain "/..", so test
  220.      if we really have to create the directory.  */
  221.  
  222.       if (stat (dirpath, &stats) && mkdir (dirpath, mode))
  223.     {
  224.       error (0, errno, "cannot create directory `%s'", dirpath);
  225.       umask (oldmask);
  226.       return 1;
  227.     }
  228.       if (verbose_fmt_string != NULL)
  229.     error (0, 0, verbose_fmt_string, dirpath);
  230.  
  231.       if (owner != (uid_t) -1 && group != (gid_t) -1)
  232.     {
  233.       if (chown (dirpath, owner, group)
  234. #ifdef AFS
  235.           && errno != EPERM
  236. #endif
  237.           )
  238.         {
  239.           error (0, errno, "%s", dirpath);
  240.           retval = 1;
  241.         }
  242.       /* chown may have turned off some permission bits we wanted.  */
  243.       if ((mode & 07000) != 0 && chmod (dirpath, mode))
  244.         {
  245.           error (0, errno, "%s", dirpath);
  246.           retval = 1;
  247.         }
  248.     }
  249.  
  250.       /* If the mode for leading directories didn't include owner "wx"
  251.      privileges, we have to reset their protections to the correct
  252.      value.  */
  253.       for (p = leading_dirs; p != NULL; p = p->next)
  254.     {
  255.       *(p->dirname_end) = '\0';
  256.       if (chmod (dirpath, parent_mode))
  257.         {
  258.           error (0, errno, "%s", dirpath);
  259.           retval = 1;
  260.         }
  261.     }
  262.     }
  263.   else
  264.     {
  265.       /* We get here if the entire path already exists.  */
  266.  
  267.       if (!S_ISDIR (stats.st_mode))
  268.     {
  269.       error (0, 0, "`%s' exists but is not a directory", dirpath);
  270.       umask (oldmask);
  271.       return 1;
  272.     }
  273.  
  274.       if (!preserve_existing)
  275.     {
  276.       /* chown must precede chmod because on some systems,
  277.          chown clears the set[ug]id bits for non-superusers,
  278.          resulting in incorrect permissions.
  279.          On System V, users can give away files with chown and then not
  280.          be able to chmod them.  So don't give files away.  */
  281.  
  282.       if (owner != (uid_t) -1 && group != (gid_t) -1
  283.           && chown (dirpath, owner, group)
  284. #ifdef AFS
  285.           && errno != EPERM
  286. #endif
  287.           )
  288.         {
  289.           error (0, errno, "%s", dirpath);
  290.           retval = 1;
  291.         }
  292.       if (chmod (dirpath, mode))
  293.         {
  294.           error (0, errno, "%s", dirpath);
  295.           retval = 1;
  296.         }
  297.     }
  298.     }
  299.  
  300.   umask (oldmask);
  301.   return retval;
  302. }
  303.